pub mod bh;
pub mod error;
pub mod compressed;

pub use bh::*;
pub use error::*;
pub use compressed::*;

use crate::{byte::*, workspace::{WorkSpace, OutputWorkSpace}};

#[derive(Debug)]
pub struct Block {
    header:BlockHeader,
    inner:BlockInner,
}

impl Block {
    pub fn header(&self) -> BlockHeader {
        self.header
    }

    pub fn serialize_into<W:ByteWrite>(&self,writer:&mut ByteWriter<W>) -> BlockResult<()> {
        writer.write_from_bytes(&self.header.into_bytes())?;
        self.inner.serialize_into(writer)?;
        Ok(())
    }

    pub fn deserialize_from<R:ByteRead>(reader:&mut ByteReader<R>, workspace:&mut WorkSpace) -> BlockResult<Self> {
        let mut three_bytes = [0u8;3];
        reader.read_to_bytes(&mut three_bytes[..])?;
        let header = BlockHeader::from_bytes(three_bytes);
        let inner = BlockInner::deserialize_from(header, reader, workspace)?;
        Ok(Self {
            header,
            inner,
        })
    }

    pub fn uncompressed_to<O:OutputStream>(&self, output:&mut O, workspace:&mut OutputWorkSpace) -> OutputResult<usize>  {
        match &self.inner {
            BlockInner::Raw(data) => Ok(output.write_from(&data)?),
            BlockInner::Rle(byte) => Ok(output.write_rle_byte(*byte, self.header.block_size() as usize)?),
            BlockInner::Compressed(cblock) => Ok(cblock.uncompressed_to(output, workspace)?),
        }
    }
}

#[derive(Debug)]
pub enum BlockInner {
    Raw(Vec<u8>),
    Rle(u8),
    Compressed(CompressedBlock),
}

impl BlockInner {
    pub fn serialize_into<W:ByteWrite>(&self,writer:&mut ByteWriter<W>) -> BlockResult<()> {
        match self {
            Self::Raw(data) => Ok(writer.write_from_bytes(&data)?),
            Self::Rle(byte) => Ok(writer.write_from_u8(byte.clone())?),
            Self::Compressed(cblock) => Ok(cblock.serialize_into(writer)?),
        }
    }

    pub fn deserialize_from<R:ByteRead>(header:BlockHeader,reader:&mut ByteReader<R>, workspace:&mut WorkSpace) -> BlockResult<Self> {
        let block_type = header.get_block_type();
        let block_size = header.block_size();
        match block_type {
            BlockType::Raw => Ok(Self::Raw(Self::decode_raw_block_from(block_size, reader)?)),
            BlockType::Rle => Ok(Self::Rle(reader.read_to_u8()?)),
            BlockType::Compressed => Ok(Self::Compressed(CompressedBlock::deserialize_from(block_size, reader, workspace)?)),
            BlockType::Reversed => panic!("block type reversed is not supported"),
        }
    }

    fn decode_raw_block_from<R:ByteRead>(block_size:u32, reader:&mut ByteReader<R>) -> BlockResult<Vec<u8>> {
        let mut data = vec![0;block_size as usize];
        reader.read_to_bytes(&mut data)?;
        Ok(data)
    }
}
